package com.github.xtranslation.core.core;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.util.ReflectUtil;
import com.github.xtranslation.core.util.CollectionUtils;
import io.vavr.control.Option;
import lombok.Getter;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;

import static io.vavr.API.*;

/**
 * TransModel: 翻译模型
 * <p>
 * 翻译框架的核心执行单元，封装了单个字段翻译所需的所有上下文信息和执行逻辑。
 * 每个 TransModel 实例代表一个具体的翻译任务，包含源对象、字段元数据、原始值等信息。
 * </p>
 * <p>
 * 核心职责：
 * 1. 封装翻译上下文：源对象、字段元数据、原始值等
 * 2. 执行翻译操作：根据翻译数据设置目标字段的值
 * 3. 支持多种数据类型：单值、集合、数组等
 * 4. 支持多种提取模式：字段提取、完整对象提取
 * </p>
 * <p>
 * 使用场景示例：
 * UserDto 中有 userId 字段，需要翻译为 userName 字段
 * TransModel 封装了 UserDto 实例、userId字段信息、userName字段信息等
 * 然后根据 userId 的值从数据源获取对应的用户信息，并提取 userName 设置到目标字段
 * </p>
 *
 * @author zhangxiaoxiang
 * @since 2025/7/27
 */
public class TransModel {

    /**
     * 完整对象提取标识
     * <p>
     * 当翻译键值设置为此标识时，表示需要提取翻译数据中的完整对象，
     * 而不是特定字段的值。
     * </p>
     * <p>
     * 使用示例：
     * // 字典翻译场景
     * // 翻译数据为: {1: "男", 2: "女"}
     * // transVal = 1 (原始值)
     * // key = "#val" (完整对象提取标识)
     * // 结果: objValue = "男" (提取完整值)
     * <p>
     * // 对象翻译场景
     * // 翻译数据为: {1001: {id: 1001, name: "张三", age: 25}}
     * // transVal = 1001 (原始值)
     * // key = "#val" (完整对象提取标识)
     * // 结果: objValue = {id: 1001, name: "张三", age: 25} (提取整个对象)
     */
    public final static String VAL_EXTRACT = "#val";

    /**
     * 翻译字段元数据
     * <p>
     * 包含翻译字段的所有元信息，如源字段、目标字段、翻译键值、翻译仓库等。
     * 是翻译操作的核心配置信息。
     * </p>
     */
    private final TransFieldMeta transFieldMeta;

    /**
     * 源字段的实际值（待翻译的值）
     * <p>
     * 从源字段中提取的实际值，用于在翻译数据中查找对应的翻译结果。
     * 例如：userId=1001，将用于在用户翻译数据中查找id为1001的用户信息。
     * </p>
     */
    @Getter
    private final Object transVal;

    /**
     * 当前处理的对象实例
     * <p>
     * 包含需要翻译字段的原始对象实例，翻译完成后会将结果设置到该对象的目标字段中。
     * </p>
     */
    @Getter
    private final Object obj;

    /**
     * 是否为多值类型标识
     * <p>
     * 标识源字段是否为集合或数组类型，决定后续处理逻辑。
     * 预先计算并缓存该值可以提高性能，避免重复类型检查。
     * </p>
     */
    @Getter
    private final boolean isMultiple;

    /**
     * 是否为完整对象提取模式标识
     * <p>
     * 当翻译键等于 VAL_EXTRACT 时为 true，表示需要提取翻译数据中的完整对象，
     * 而不是特定字段的值。
     * </p>
     */
    @Getter
    private final boolean isValExtract;

    /**
     * TransModel 构造函数
     * <p>
     * 在创建实例时初始化所有必要的上下文信息，包括：
     * 1. 判断字段类型（单值/多值）
     * 2. 提取源字段值
     * 3. 判断提取模式（字段提取/完整对象提取）
     * </p>
     *
     * @param obj            需要进行翻译的对象实例
     * @param transFieldMeta 翻译字段的元数据信息
     */
    public TransModel(Object obj, TransFieldMeta transFieldMeta) {
        this.transFieldMeta = transFieldMeta;
        this.obj = obj;
        Field transField = transFieldMeta.getTransField();
        Class<?> type = transField.getType();
        // 预先判断是否为多值类型，提高后续处理性能
        this.isMultiple = (Iterable.class).isAssignableFrom(type) || type.isArray();
        // 提取源字段的实际值
        this.transVal = Opt.ofTry(() -> ReflectUtil.getFieldValue(this.obj, transField)).orElse(null);
        // 判断是否为完整对象提取模式
        this.isValExtract = VAL_EXTRACT.equals(this.transFieldMeta.getKey());
    }

    /**
     * 执行翻译操作并设置目标字段值
     * <p>
     * 根据提供的翻译数据映射，执行实际的翻译操作，将翻译结果设置到目标字段中。
     * 支持多种复杂场景：
     * 1. 单值翻译 vs 多值翻译
     * 2. 字段提取 vs 完整对象提取
     * 3. 集合类型 vs 数组类型 vs 普通类型
     * </p>
     *
     * @param idValueMap 包含翻译值和对象值的映射，键为源值，值为翻译数据对象
     */
    public void setValue(Map<Object, Object> idValueMap) {
        // 将传入的映射转换为对象值映射，便于后续字段提取
        // 这一步将翻译数据对象转换为Map格式，提高字段访问效率
        Map<Object, ? extends Map<?, ?>> objValMap = idValueMap.entrySet().stream()
                .collect(Collectors.toMap(Map.Entry::getKey, entry -> BeanUtil.beanToMap(entry.getValue())));

        // 根据是否为多值类型采用不同的处理逻辑
        Object objValue = this.isMultiple ? handleMultipleValue(objValMap) : handleSingleValue(objValMap);

        // 如果对象值不为空，则设置对象字段的值
        Opt.ofNullable(objValue).ifPresent(val -> ReflectUtil.setFieldValue(this.obj, this.transFieldMeta.getField(), val));
    }

    /**
     * 处理多值场景（集合或数组类型）
     */
    private Object handleMultipleValue(Map<Object, ? extends Map<?, ?>> objValMap) {
        // 获取多个转换值
        List<Object> multipleTransVal = this.getMultipleTransVal();
        // 获取对象值（根据目标字段类型创建合适的容器）相当于 new 对象的操作
        Object objValue = this.getObjValue(multipleTransVal);

        // 使用 Map 存储类型处理器
        Map<Class<?>, java.util.function.Consumer<Object>> handlers = new HashMap<>();
        handlers.put(Collection.class, obj -> {
            @SuppressWarnings("unchecked")
            Collection<Object> objCollection = (Collection<Object>) obj;
            handleCollectionValue(objValMap, multipleTransVal, objCollection);
        });
        handlers.put(Object[].class, obj -> handleArrayValue(objValMap, multipleTransVal, (Object[]) obj));

        // 应用对应的处理器
        handlers.entrySet().stream()
                .filter(entry -> entry.getKey().isInstance(objValue))
                .findFirst()
                .ifPresent(entry -> entry.getValue().accept(objValue));

        return objValue;
    }

    /**
     * 处理单值场景（普通类型）
     */
    private Object handleSingleValue(Map<Object, ? extends Map<?, ?>> objValMap) {
        return this.isValExtract
                ? extractSingleValueFromAll(objValMap)
                : extractSingleValueByKey(objValMap);
    }

    /**
     * 处理集合类型值
     */
    private void handleCollectionValue(Map<Object, ? extends Map<?, ?>> objValMap,
                                       List<Object> multipleTransVal,
                                       Collection<Object> objCollection) {
        multipleTransVal.forEach(val -> {
            Object value = this.isValExtract
                    ? extractCollectionValueFromAll(objValMap, val)
                    : extractCollectionValueByKey(objValMap, val);
            Opt.ofNullable(value).ifPresent(objCollection::add);
        });
    }

    /**
     * 处理数组类型值
     */
    private void handleArrayValue(Map<Object, ? extends Map<?, ?>> objValMap,
                                  List<Object> multipleTransVal,
                                  Object[] objArray) {
        for (int i = 0; i < multipleTransVal.size() && i < objArray.length; i++) {
            final int index = i;
            Object val = multipleTransVal.get(index);
            Object value = this.isValExtract
                    ? extractArrayValueFromAll(objValMap, val)
                    : extractArrayValueByKey(objValMap, val);
            Opt.ofNullable(value).ifPresent(v -> objArray[index] = v);
        }
    }

    /**
     * 从所有值中提取单值（完整对象提取模式）
     */
    private Object extractSingleValueFromAll(Map<Object, ? extends Map<?, ?>> objValMap) {
        return objValMap.values().stream()
                .map(objMap -> objMap.get(this.transVal))
                .filter(Objects::nonNull)
                .findFirst()
                .orElse(null);
    }

    /**
     * 根据键提取单值（字段提取模式）
     */
    private Object extractSingleValueByKey(Map<Object, ? extends Map<?, ?>> objValMap) {
        return Opt.ofNullable(objValMap.get(this.transVal))
                .map(objMap -> objMap.get(this.transFieldMeta.getKey()))
                .orElse(null);
    }

    /**
     * 从所有值中提取集合值（完整对象提取模式）
     */
    private Object extractCollectionValueFromAll(Map<Object, ? extends Map<?, ?>> objValMap, Object val) {
        return objValMap.values().stream()
                .map(objMap -> objMap.get(val))
                .filter(Objects::nonNull)
                .findFirst()
                .orElse(null);
    }

    /**
     * 根据键提取集合值（字段提取模式）
     */
    private Object extractCollectionValueByKey(Map<Object, ? extends Map<?, ?>> objValMap, Object val) {
        return Opt.ofNullable(objValMap.get(val))
                .map(objMap -> objMap.get(this.transFieldMeta.getKey()))
                .orElse(null);
    }

    /**
     * 从所有值中提取数组值（完整对象提取模式）
     */
    private Object extractArrayValueFromAll(Map<Object, ? extends Map<?, ?>> objValMap, Object val) {
        return objValMap.values().stream()
                .map(objMap -> objMap.get(val))
                .filter(Objects::nonNull)
                .findFirst()
                .orElse(null);
    }

    /**
     * 根据键提取数组值（字段提取模式）
     */
    private Object extractArrayValueByKey(Map<Object, ? extends Map<?, ?>> objValMap, Object val) {
        return Opt.ofNullable(objValMap.get(val))
                .map(objMap -> objMap.get(this.transFieldMeta.getKey()))
                .orElse(null);
    }


    /**
     * 根据多个转换值获取对象值容器
     * <p>
     * 根据目标字段的类型创建合适的容器来存储翻译结果。
     * 如果目标字段已有值则直接使用，否则根据类型创建新的容器实例。
     * 这种设计既保证了类型兼容性，又支持了复用已有容器的场景。
     * </p>
     *
     * @param multipleTransVal 多个转换值，用于确定数组大小
     * @return 适当类型的对象值容器
     */
    private Object getObjValue(List<Object> multipleTransVal) {
        // 获取字段当前值
        Object objValue = ReflectUtil.getFieldValue(this.obj, this.transFieldMeta.getField());

        // 构造对应的对象值，如果已有则直接使用，否则根据类型创建新实例
        return Option.of(objValue).getOrElse(() -> {
            Class<?> type = this.transFieldMeta.getField().getType();
            return Match(type).of(
                    Case($(List.class::isAssignableFrom), new ArrayList<>()),
                    Case($(Set.class::isAssignableFrom), new HashSet<>()),
                    Case($(Class::isArray), Opt.ofNullable(type.getComponentType()).map(t -> Array.newInstance(t, multipleTransVal.size())).orElse(null)),
                    Case($(), () -> null));
        });
    }


    /**
     * 获取翻译字段的元数据信息
     *
     * @return 翻译字段元数据
     */
    public TransFieldMeta getTransField() {
        return transFieldMeta;
    }

    /**
     * 获取多个转换值
     * <p>
     * 使用工具类将源值转换为List格式，统一处理单值和多值场景。
     * </p>
     *
     * @return 转换值列表
     */
    public List<Object> getMultipleTransVal() {
        return CollectionUtils.objToList(this.transVal);
    }

    /**
     * 判断是否需要进行翻译
     * <p>
     * 通过检查源值是否为空来判断，为空则无需翻译。
     * </p>
     *
     * @return true表示需要翻译，false表示无需翻译
     */
    public boolean needTrans() {
        return transVal != null;
    }

}
